package away3d.animators.data ;


import away3d.materials.passes.MaterialPassBase ;
import away3d.materials.utils.AGAL;
import flash.display3D.Context3D;



/**
 * VertexAnimation defines an animation type that blends different poses (geometries) together to create a final pose.
 */
public class VertexAnimation extends AnimationBase
{
    
    
    public var _streamIndex : Int ;
    public var _useNormals  : Bool ;
    public var _useTangents : Bool ;
    public var _numPoses    : Int ;
    private var _blendMode  : String ;
    
    
    
    /**
     * Creates a new VertexAnimation object
     * @param numPoses The amount of poses to be blended together.
     * @param blendMode The type of blending to be performed by the animation. The following values are supported:
     * <ul>
     * <li>VertexAnimationMode.ADDITIVE: The pose is generated from a base mesh and a number of additive "difference" poses.</li>
     * <li>VertexAnimationMode.ABSOLUTE: The pose is generated by a weighted average of a number of poses.</li>
     * </ul>
     */
    public function new( numPoses : Int, blendMode : String )
    {
        
        super() ;
        _numPoses   = numPoses ;
        _blendMode  = blendMode ;
        
    }
    
    public var blendMode( get_blendMode, null ): String ;
    
    /**
     * The type of blending to be performed by the animation.
     */
    public function get_blendMode() : String
    {
        
        return _blendMode ;
        
    }
    
    
    /** 
     * @inheritDoc  
     */
    override public function createAnimationState() : AnimationStateBase
    {
        
        return new VertexAnimationState( this ) ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function deactivate( context: Context3D, pass: MaterialPassBase )
    {
        
        context.setVertexBufferAt( _streamIndex, null ) ;
        if( _useNormals ) context.setVertexBufferAt( _streamIndex + 1, null ) ;
        if( _useTangents ) context.setVertexBufferAt( _streamIndex + 2, null ) ;
        
    }
    
    
    /**
     * @inheritDoc
     */ 
    override public function getAGALVertexCode( pass: MaterialPassBase ) : String
    {
        
        if( _blendMode == VertexAnimationMode.ABSOLUTE ) return getAbsoluteAGALCode( pass ) ;
        
        return getAdditiveAGALCode( pass ) ;
        
    }
    
    
    /**
     * Generates the vertex AGAL code for absolute blending.
     */
    private function getAbsoluteAGALCode( pass: MaterialPassBase ) : String
    {
        
        var attribs     = pass.getAnimationSourceRegisters() ;
        var targets     = pass.getAnimationTargetRegisters() ;
        var code        = "" ;
        var temp1       = findTempReg( targets ) ;
        var temp2       = findTempReg( targets, temp1 ) ;
        var regs        = [ "x", "y", "z", "w" ] ;
        var len         = attribs.length ;
        _useNormals     = len > 1;
        _useTangents    = len > 2;
        
        if (len > 2) len = 2 ;
        
        _streamIndex    = pass.numUsedStreams ;
        var k : Int ;
        for ( i in 0...len )
        {
            
            for( j in 0..._numPoses )
            {
                if( j == 0 )
                {
                    
                    code += AGAL.mul( temp1, attribs[ i ], "vc" + pass.numUsedVertexConstants + "." + regs[ j ] );
                    
                }
                else
                {
                    
                    code += AGAL.mul( temp2, "va" + ( _streamIndex + k ), "vc" + pass.numUsedVertexConstants + "." + regs[ j ] );
                    if( j < _numPoses - 1 ) 
                    {
                        
                        code += AGAL.add( temp1, temp1, temp2 );
                        
                    }
                    else 
                    {
                        
                        code += AGAL.add( targets[ i ], temp1, temp2 );
                        ++k;
                        
                    }
                    
                }
                
            }
            
        }
        
        if( _useTangents )
        {
            
            //TODO: Do we need these uint here??? need to fix
            code += AGAL.dp3( temp1 + ".x", attribs[ uint( 2 ) ], targets[ uint( 1 ) ] ) ;
            code += AGAL.mul( temp1, targets[ uint( 1 ) ], temp1 + ".x" ) ;
            code += AGAL.sub( targets[ uint( 2 ) ], attribs[ uint( 2 ) ], temp1 ) ;
            
        }
        
        return code;
        
    }
    
    
    private function getAdditiveAGALCode( pass : MaterialPassBase ) : String
    {
        
        var attribs     = pass.getAnimationSourceRegisters() ;
        var targets     = pass.getAnimationTargetRegisters() ;
        var code        = "" ;
        var len         = attribs.length ;
        var regs        = [ "x", "y", "z", "w" ] ;
        var temp1       = findTempReg( targets ) ;
        
        var k : Int;
        
        _useNormals     = len > 1;
        _useTangents    = len > 2;
        
        if( len > 2 ) len = 2 ;
        
        code += AGAL.mov( targets[ 0 ], attribs[ 0 ] ) ;
        
        if( _useNormals ) code += AGAL.mov( targets[ 1 ], attribs[ 1 ] ) ;
        
        for( i in 0...len )
        {
            
            for( j in 0..._numPoses )
            {
                
                code += AGAL.mul( temp1, "va" + ( _streamIndex + k ), "vc" + pass.numUsedVertexConstants + "." + regs[ j ] ) ;
                code += AGAL.add( targets[ i ], targets[ i ], temp1 ) ;
                k++ ;
                
            }
            
        }
        
        if( _useTangents )
        {
            //TODO: Do we need these uint here??? need to fix
            code += AGAL.dp3( temp1 + ".x", attribs[ uint( 2 ) ], targets[ uint( 1 ) ] ) ;
            code += AGAL.mul( temp1, targets[ uint( 1 ) ], temp1 + ".x") ;
            code += AGAL.sub( targets[ uint( 2 ) ], attribs[ uint( 2 ) ], temp1 ) ;
            
        }
        
        return code;
        
    }
    
    
    /**
     * Retrieves a temporary register that's still free.
     * @param exclude An array of non-free temporary registers
     * @param excludeAnother An additional register that's not free
     * @return A temporary register that can be used
     */
    private function findTempReg( exclude: Array, ?excludeAnother: String = null ) : String
    {
        
        var i   : Int ;
        var reg : String ;
        
        while( true )
        {
            reg = "vt" + i;
            if( exclude.indexOf( reg ) == -1 && excludeAnother != reg ) return reg ;
            ++i ;
        }
        
        // can't be reached
        return null ;
        
    }
    
    
}